using CacheManager.Core;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SSPivot.Common.Cache
{
    /// <summary>
    /// 提供本地内存缓存和Redis分布式缓存的双层缓存服务实现
    /// 支持三种缓存模式：LocalOnly(仅本地)、RedisOnly(仅Redis)、Both(两者同时使用)
    /// </summary>
    public class CacheService : ICacheService
    {
        private readonly IMemoryCache _localCache; // 本地内存缓存实例
        private readonly IDatabase _redisDb;      // Redis数据库连接
        private readonly IConnectionMultiplexer _connectionMultiplexer;
        private readonly CacheMode _mode;         // 当前缓存模式

        /// <summary>
        /// 初始化缓存服务
        /// </summary>
        /// <param name="config">缓存配置</param>
        /// <exception cref="ArgumentNullException">当config为null时抛出</exception>
        /// <exception cref="RedisConnectionException">当Redis连接失败时抛出</exception>
        public CacheService(CacheConfig config)
        {
            _mode = config.Mode;
            _localCache = new MemoryCache(new MemoryCacheOptions());

            if (_mode != CacheMode.LocalOnly)
            {
                var redis = ConnectionMultiplexer.Connect(config.RedisConnection);
                _connectionMultiplexer= redis;
                _redisDb = redis.GetDatabase();
            }
        }

        /// <summary>
        /// 从缓存中获取指定键的值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <returns>
        /// 如果找到则返回对应的值，否则返回default(T)
        /// 在Both模式下，会先从本地缓存查找，未找到则查Redis并回填本地缓存
        /// </returns>
        public T Get<T>(string key)
        {
            // 本地缓存检查
            if (_mode != CacheMode.RedisOnly && _localCache.TryGetValue(key, out T localValue))
            {
                return localValue;
            }

            // Redis检查
            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                    var redisValue = _redisDb.StringGet(key);
                    if (!redisValue.IsNull)
                    {
                        var value = Deserialize<T>(redisValue);
                        if (_mode == CacheMode.Both)
                        {
                            _localCache.Set(key, value);
                        }
                        return value;
                    }
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
            }

            return default;
        }
        public async Task<T> GetAsync<T>(string key)
        { 
            // Redis检查
            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                    var redisValue = await _redisDb.StringGetAsync(key);
                    if (!redisValue.IsNull)
                    {
                        var value = Deserialize<T>(redisValue);
                        if (_mode == CacheMode.Both)
                        {
                            _localCache.Set(key, value);
                        }
                        return value;
                    }
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
            }
            else
            {
                _localCache.TryGetValue(key, out T localValue);
                return await Task.FromResult(localValue);
            }

            return await Task.FromResult(default(T));
        }
        public async Task<T> GetOrAddAsync<T>(string key, Func<Task<T>> factory, TimeSpan? timeSpan = null)
        {
            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                    var redisValue = await _redisDb.StringGetAsync(key);
                    if (!redisValue.IsNull)
                    {
                        var value = Deserialize<T>(redisValue);
                        if (_mode == CacheMode.Both)
                        {
                            _localCache.Set(key, value);
                        }
                        return value;
                    }
                    else
                    {
                        var item = await factory.Invoke();
                        await _redisDb.StringSetAsync(key, Serialize(item), timeSpan);
                        return item;
                    }
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
                return await Task.FromResult(default(T));
            }
            else
            {
                _localCache.TryGetValue(key, out T localValue);
                if (localValue == null)
                {
                    localValue = await factory.Invoke();
                    _localCache.Set(key, localValue, timeSpan ?? TimeSpan.FromMinutes(5));
                }
                return await Task.FromResult(localValue);
            }
        }

        public T GetOrAdd<T>(string key, Func<T> factory, TimeSpan? timeSpan = null)
        {
            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                    var redisValue = _redisDb.StringGet(key);
                    if (!redisValue.IsNull)
                    {
                        var value = Deserialize<T>(redisValue);
                        if (_mode == CacheMode.Both)
                        {
                            _localCache.Set(key, value);
                        }
                        return value;
                    }
                    else
                    {
                        var item = factory.Invoke();
                        _redisDb.StringSet(key, Serialize(item), timeSpan);
                        return item;
                    }
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
                return default(T);
            }
            else
            {
                _localCache.TryGetValue(key, out T localValue);
                if (localValue == null)
                {
                    localValue = factory.Invoke();
                    _localCache.Set(key, localValue, timeSpan ?? TimeSpan.FromMinutes(5));
                }
                return localValue;
            }
        }
        /// <summary>
        /// 设置缓存值
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="value">要缓存的值</param>
        /// <param name="expiry">过期时间，null表示使用默认5分钟</param>
        /// <remarks>
        /// 根据当前缓存模式，会同时或单独更新本地缓存和Redis缓存
        /// </remarks>
        public void Set<T>(string key, T value, TimeSpan? expiry = null)
        {
            if (_mode != CacheMode.RedisOnly)
            {
                _localCache.Set(key, value, expiry ?? TimeSpan.FromMinutes(5));
            }

            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                    _redisDb.StringSet(key, Serialize(value), expiry);
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
            }
        }
        public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
        { 

            if (_mode != CacheMode.LocalOnly)
            {
                try
                {
                   await _redisDb.StringSetAsync(key, Serialize(value), expiry);
                }
                catch (RedisException ex)
                {
                    Console.WriteLine($"Redis操作失败: {ex.Message}");
                }
            }
            else
            {
                _localCache.Set(key, value, expiry ?? TimeSpan.FromMinutes(5));
                await Task.CompletedTask;
            }
        }

        /// <summary>
        /// 检查指定键是否存在于缓存中
        /// </summary>
        /// <param name="key">要检查的缓存键</param>
        /// <returns>
        /// 如果键存在返回true，否则返回false
        /// 在Both模式下，只要任一缓存中存在即返回true
        /// </returns>
        public bool Exists(string key)
        {
            if (_mode == CacheMode.LocalOnly) return _localCache.TryGetValue(key, out _);
            if (_mode == CacheMode.RedisOnly) return _redisDb.KeyExists(key);
            return _localCache.TryGetValue(key, out _) || _redisDb.KeyExists(key);
        }
        public async Task<bool> ExistsAsync(string key)
        {
            var result = false;
            if (_mode == CacheMode.LocalOnly) result = await Task.FromResult( _localCache.TryGetValue(key, out _));
            if(!result)
                if (_mode == CacheMode.RedisOnly) result = await _redisDb.KeyExistsAsync(key);

            return result;
        }

        /// <summary>
        /// 从缓存中移除指定键
        /// </summary>
        /// <param name="key">要移除的缓存键</param>
        /// <remarks>
        /// 根据当前缓存模式，会同时或单独从本地缓存和Redis中移除
        /// </remarks>
        public void Remove(string key)
        {
            if (_mode != CacheMode.RedisOnly) _localCache.Remove(key);
            if (_mode != CacheMode.LocalOnly) _redisDb.KeyDelete(key);
        }
        public async Task RemoveAsync(string key)
        { 
            if (_mode != CacheMode.LocalOnly)
            {
                await _redisDb.KeyDeleteAsync(key);
            }
            else
            {
                _localCache.Remove(key);
                await Task.CompletedTask;
            }
           
        }

        /// <summary>
        /// 获取Redis数据库实例(当使用Redis时)
        /// </summary>
        public IDatabase RedisDatabase => _redisDb;
        /// <summary>
        /// ConnectionMultiplexer
        /// </summary>
        public IConnectionMultiplexer ConnMultiplexer => _connectionMultiplexer;

        /// <summary>
        /// 获取是否启用了分布式缓存
        /// </summary>
        public bool IsDistributedCacheEnabled => _mode != CacheMode.LocalOnly;

        /// <summary>
        /// 序列化对象为JSON字符串
        /// </summary>
        private string Serialize<T>(T obj) => JsonConvert.SerializeObject(obj);
        
        /// <summary>
        /// 从JSON字符串反序列化对象
        /// </summary>
        private T Deserialize<T>(string json) => JsonConvert.DeserializeObject<T>(json);

      
    }
}
